﻿#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<memory>
using namespace std;

/*
	程序new了以后，也进行delete，但是因为抛异常导致后续的delete没有得到执行，故内存泄漏，所以需要new以后捕获异常，
	捕获到异常后delete内存，再把异常抛出，因为new本身也可能抛异常，连续的两个new和下面的Divide都可能会抛异常，处
	理起来很麻烦。智能指针放到这样的场景里面就让问题简单多了。
*/


double Divide(int a, int b)
{
	// 当b == 0时抛出异常 
	if (b == 0)
	{
		throw "Divide by zero condition!";
	}

	else
	{
		return (double)a / (double)b;
	}
}

#if 0

void Func()
{
	// 这里可以看到如果发生 除0错误抛出异常，另外下面的array和array2没有得到释放。 
	// 所以这里捕获异常后并不处理异常，异常还是交给外面处理，这里捕获了再重新抛出去。 
	// 但是如果array2  new的时候抛异常呢，就还需要套一层捕获释放逻辑，这里更好解决方案是智能指针

	int* array1 = new int[10];
	int* array2 = new int[10];   // 注意：new自身也会抛异常 
	
	try
	{
		int len, time;
		cin >> len >> time;
		cout << Divide(len, time) << endl;
	}

	catch (...)
	{
		cout << "delete []" << array1 << endl;
		cout << "delete []" << array2 << endl;

		delete[] array1;
		delete[] array2;
		throw; // 异常重新抛出，捕获到什么抛出什么 
	}

	// ...
	cout << "delete []" << array1 << endl;
	delete[] array1;
	cout << "delete []" << array2 << endl;
	delete[] array2;
}

#endif


//写成类模板   哪个类型的资源都可以管理
template<class T>
class SmartPtr
{
public:
	//RAII
	SmartPtr(T* ptr)
		:_ptr(ptr)
	{}

	~SmartPtr()
	{
		cout << "delete[]" << _ptr << endl;
		delete[] _ptr;
	}

	//重载 []   operator[]
	T& operator[] (size_t i)
	{
		return _ptr[i];
	}

	//重载 *    operator*
	T& operator*()
	{
		return *_ptr;
	}

	//重载 ->
	T* operator->()
	{
		return _ptr;
	}

private:
	T* _ptr;
};
//思路：资源通常在指针里面，在初始化列表的时候传过去，保存起来，再析构的时候对他进行delete


//上面的代码就可以修改为    
void Func()
{
	SmartPtr<int> sp1 = new int[10];     //这是因为new返回的本就是一个指针   可以直接给他管理

	int* array2 = new int[10];
	SmartPtr<int> sp2(array2);    //不然就是下面的两行

	//类型二   pair类型
	SmartPtr<pair<int, int>> sp3 = new pair<int, int>[10];

	int len, time;
	cin >> len >> time;
	cout << Divide(len, time) << endl;

	//对上面的 pair类型进行访问
	sp1[5] = 50;
	sp3->first = 1;
	sp3->second = 2;
	cout << sp1[5] << endl;
	cout << sp3->first << "   " << sp3->second << endl;
}



/*

int main()
{
	try
	{
		Func();
	}

	catch (const char* errmsg)
	{
		cout << errmsg << endl;
	}

	catch (const exception& e)
	{
		cout << e.what() << endl;
	}

	catch (...)
	{
		cout << "未知异常" << endl;
	}

	return 0;
}

*/

struct Date
{
public:
Date(int year=1,int month=1,int day=1)
	:_year(year),
	_month(month),
	_day(day)
	{}

//析构
	~Date()
	{
		cout << "~Date()" << endl;
	}

	int _year;
	int _month;
	int _day;
};


int main()
{
/*
//auto_ptr
	auto_ptr<Date> ap1(new Date);
	//拷贝时，管理权限转移，被拷贝对象ap1悬空
	auto_ptr<Date> ap2(ap1);    //最终会发现   只析构一次   想访问ap1  但是会发现ap1  是悬空了的指针  为空

	//可以移动构造是因为之前对应的都是右值   右值 将亡值对象都是一些自定义类型的匿名对象  临时对象
	//可以直接转走   因为没有对象能访问它
	ap1->_year++;
	
*/


/*
//unique_ptr (不支持拷贝)
	unique_ptr<Date> up1(new Date);
	
	//不支持拷贝
	//unique_ptr<Date> up2(up1);
	
	//支持移动   但是移动后up1也悬空   所以移动需要谨慎
	unique_ptr<Date> up3(move(up1));

*/


/*
//shared_ptr（支持拷贝  也支持移动）
	shared_ptr<Date> sp1(new Date);
	
	//支持拷贝
	shared_ptr<Date> sp2(sp1);
	shared_ptr<Date> sp3(sp2);
	cout << sp1.use_count() << endl;
	sp1->_year++;
	cout << sp1->_year << endl;
	cout << sp2->_year << endl;
	cout << sp3->_year << endl;

*/

	return 0;
}


#if 0

/* 注意：C++ 智能指针虽好，但是拷贝是一个复杂点     这是因为涉及到深拷贝 */
int main()
{
	//需要sp1和sp2 共同管理资源   浅拷贝  析构多次问题得解决
	SmartPtr<int> sp1 = new int[10];
	SmartPtr<int> sp2(sp1);     //这样就导致了浅拷贝的问题   默认是值拷贝（浅拷贝）

	/* 智能指针模拟的是原身指针的行为  实际是期待 sp1和sp2共同管理同一块资源 */

	return 0;
}

#endif